package cn.icanci.loopstack.rec.engine.script.context;

import cn.icanci.loopstack.rec.common.enums.ScriptTypeEnum;

import java.io.Serializable;
import java.util.StringJoiner;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;

/**
 * 脚本引擎上下文
 * 脚本一次执行的结果信息等
 * 
 * @author icanci
 * @since 1.0 Created in 2022/11/13 12:42
 */
public class RecScriptEngineContext<T> implements Serializable {

    private static final long serialVersionUID = 6215311466648575581L;

    /**
     * 脚本类型
     */
    private ScriptTypeEnum    scriptType;
    /**
     * 脚本内容
     */
    private String            scriptContent;
    /**
     * 脚本执行引擎
     */
    private ScriptEngine      scriptEngine;
    /**
     * 脚本执行引擎编译器，如果脚本执行引擎实现了Compilable
     * 那么 scriptEngine 和 compilable 则是同一个对象
     */
    private Compilable        compilable;
    /**
     * 通过 Compilable 编译 scriptContent 之后的实例对象
     */
    private CompiledScript    compiledScript;
    /**
     * 脚本执行之后的结果
     */
    private T                 retVal;
    /**
     * 脚本执行出错之后的结果
     */
    private Throwable         throwable;
    /**
     * 脚本执行结果返回类型，如果指定了此类型
     */
    private Class<T>          type;
    /**
     * 脚本执行请求参数
     */
    private Bindings          bindings;

    // ============================ ability method ============================

    /**
     * 是否执行成功
     *
     * @return 返回是否执行成功
     */
    public boolean isSuccess() {
        return retVal != null && throwable == null;
    }

    /**
     * 获取真正的执行结果，并且对返回结果进行校验
     * 
     * @return 获取真正的执行结果
     */
    public T getRealRetVal() {
        try {
            if (retVal == null) {
                throw new NullPointerException("The script engine execution result is null!");
            }

            if (type == Integer.class) {
                return (T) Integer.valueOf(String.valueOf(retVal));
            }
            if (type == Double.class) {
                return (T) Double.valueOf(String.valueOf(retVal));
            }
            if (type == Long.class) {
                return (T) Long.valueOf(String.valueOf(retVal));
            }
            if (type == String.class) {
                return (T) String.valueOf(retVal);
            }
            // 非标准类型返回对象，此类归属脚本执行结果返回
            return retVal;
        } catch (Throwable e) {
            if (this.throwable != null) {
                this.throwable.addSuppressed(e);
            }
            this.throwable = e;
        }
        return null;
    }

    // ============================ getter/setter ============================
    public ScriptTypeEnum getScriptType() {
        return scriptType;
    }

    public void setScriptType(ScriptTypeEnum scriptType) {
        this.scriptType = scriptType;
    }

    public String getScriptContent() {
        return scriptContent;
    }

    public void setScriptContent(String scriptContent) {
        this.scriptContent = scriptContent;
    }

    public ScriptEngine getScriptEngine() {
        return scriptEngine;
    }

    public void setScriptEngine(ScriptEngine scriptEngine) {
        this.scriptEngine = scriptEngine;
    }

    public Compilable getCompilable() {
        return compilable;
    }

    public void setCompilable(Compilable compilable) {
        this.compilable = compilable;
    }

    public CompiledScript getCompiledScript() {
        return compiledScript;
    }

    public void setCompiledScript(CompiledScript compiledScript) {
        this.compiledScript = compiledScript;
    }

    public void setRetVal(T retVal) {
        this.retVal = retVal;
    }

    public Throwable getThrowable() {
        return throwable;
    }

    public void setThrowable(Throwable throwable) {
        this.throwable = throwable;
    }

    public Class<T> getType() {
        return type;
    }

    public void setType(Class<T> type) {
        this.type = type;
    }

    public Bindings getBindings() {
        return bindings;
    }

    public void setBindings(Bindings bindings) {
        this.bindings = bindings;
    }

    @Override
    public String toString() {
        return new StringJoiner(",").add("scriptType=" + scriptType).add("scriptContent=" + scriptContent).add("scriptEngine=" + scriptEngine).add("compilable=" + compilable)
            .add("compiledScript=" + compiledScript).add("retVal=" + retVal).add("throwable=" + throwable).add("type=" + type).add("bindings=" + bindings).toString();
    }
}
